﻿using Furion.EventBus;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;

namespace GoodAdmin.System;

/// <inheritdoc cref="IMenuService"/>
public class MenuService : DbRepository<SysMenu>, IMenuService, ITransient
{
    private readonly IRedisCacheManager _redisCacheManager;
    private readonly IRelationService _relationService;
    private readonly IEventPublisher _eventPublisher;
    public MenuService(IRedisCacheManager redisCacheManager,IRelationService relationService, IEventPublisher eventPublisher)
    {
        _redisCacheManager = redisCacheManager;
        _relationService = relationService;
        _eventPublisher = eventPublisher;
    }

    /// <inheritdoc/>
    public async Task AddMenu(MenuAddInput input)
    {
        await CheckInput(input,CommonConst.ACTION_INSERT);
        if(await InsertAsync(input))
        {
            RefreshCache();
        }
    }

    /// <inheritdoc/>
    public async Task UpdateMenu(MenuUpdateInput input)
    {
        await CheckInput(input, CommonConst.ACTION_UPDATE);
        var menu = await GetMenuDetail(input.Id);
        if(await UpdateAsync(input))
        {
            RefreshCache();
            //当修改了上级之后，需要重置角色、数据范围等缓存
            if (menu.ParentId != input.ParentId)
            {
                await RemoveDataScopeCache(new List<long> { input.Id });
            }            
        }
    }
    /// <inheritdoc/>
    public async Task DeleteMenu(MenuDeleteInput input)
    {
        var menuList = await GetMenuList();
        if (menuList.Any(it => it.SystemMark == CommonConst.SYSTEM))
        {
            throw Oops.Bah(MenuErrorCode.M005);
        }
        //查询到所有下级
        var child = await GetChildList(input.Id,true);
        var menuIdList = child.Select(it => it.Id).ToList();
        if (await DeleteAsync(it=>it.Id==input.Id))
        {
            //删除角色与菜单的关联关系
            await _relationService.DeleteRelationByCategoryAndTargetId(CategoryConst.RELATION_ROLE_MENU, menuIdList);
            RefreshCache();
            await RemoveDataScopeCache(menuIdList);
        }
    }
    /// <inheritdoc/>
    public async Task<SysMenu> GetMenuDetail(long menuId)
    {
        var menuList = await GetMenuList();
        return menuList.FirstOrDefault(it => it.Id == menuId);
    }
    /// <inheritdoc/>
    public async Task<List<SysMenu>> GetMenuList()
    {
        var key = CacheConst.SysMenu;
        var menuList = _redisCacheManager.Get<List<SysMenu>>(key);
        if (menuList == null)
        {
            menuList = await Context.Queryable<SysMenu>().OrderBy(it=>it.SortCode).ToListAsync();
            _redisCacheManager.Set(key, menuList);
        }
        return menuList;
    }

    public async Task<List<SysMenu>> GetMenuTree()
    {
        var menuList = await GetMenuList();
        menuList.ForEach(it => it.Meta = new MenuMeta { Title = it.Title, IsKeepAlive = it.IsKeepAlive, Icon = it.Icon,IsLink=it.IsLink });
        return ConstructMenuTree(menuList);
    }
    /// <inheritdoc/>
    public async Task<List<SysMenu>> GetMenuListByMenuId(List<long> IdList)
    {
        var menuList = await GetMenuList();
        var result = menuList.Where(m => IdList.Contains(m.Id)).ToList();
        return result;
    }

    /// <inheritdoc/>
    public async Task<List<SysMenu>> GetMenuListByRoleId(long roleId)
    {
        var menuIds= (await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_MENU,roleId)).Select(r=>r.TargetId).ToList();
        var menuList = await GetMenuListByMenuId(menuIds);
        return menuList;

    }

    /// <inheritdoc/>
    public List<SysMenu> ConstructMenuTree(List<SysMenu> menuList, long ParentId = 0)
    {
        var menus = menuList.Where(it => it.ParentId == ParentId).ToList();

        if (menus.Count == 0) return menus;
        foreach(var menu in menus)
        {
            menu.Children = ConstructMenuTree(menuList, menu.Id);
        }
        return menus;
    }
    
    /// <inheritdoc/>
    public List<ApiResource> GetResource(List<string> apiRoutes)
    {
        List<ApiResource> apiResources = new();
        // 获取所有需要数据范围权限的控制器
        var controllerTypes = App.EffectiveTypes.
            Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RoleDataScopeAttribute), false));
        foreach (var controller in controllerTypes)
        {
            //获取数据权限特性
            var routeAttribute = controller.GetCustomAttribute<RouteAttribute>();
            if (routeAttribute == null)
                continue;
            var route = routeAttribute;//取第一个值
            var routeName = GetRouteName(controller.Name, route.Template);//赋值路由名称
            //如果路由包含在路由列表中
            if (!apiRoutes.Contains(routeName)) 
                continue;
                
            //获取所有方法
            var menthods = controller.GetMethods();
            //遍历方法
            foreach (var menthod in menthods)
            {
                //获取接口描述
                var description = menthod.GetCustomAttribute<DisplayNameAttribute>();
                if(description == null) {
                    continue;
                }
                //获取忽略数据权限特性
                var ignoreRolePermission = menthod.GetCustomAttribute<IgnoreRoleDataScopeAttribute>();
                if (ignoreRolePermission != null) continue;//如果是空的代表需要数据权限
                                       
                //默认路由名称
                var apiRoute = StringUtils.FirstCharToLower(menthod.Name);
                //获取get特性
                var requestGet = menthod.GetCustomAttribute<HttpGetAttribute>();
                if (requestGet != null)//如果是get方法
                    apiRoute = requestGet.Template;
                else
                {
                    //获取post特性
                    var requestPost = menthod.GetCustomAttribute<HttpPostAttribute>();
                    if (requestPost != null)//如果是post方法
                        apiRoute = requestPost.Template;
                }
                apiRoute = route.Template + $"/{apiRoute}";                                
                var apiName = description.DisplayName;//接口名称用描述的名称

                //合并
                var resourceName = apiRoute + $"[{apiName}]";
                //添加到权限列表
                apiResources.Add(new ApiResource
                {
                    ApiName = apiName,
                    ApiRoute = apiRoute,
                    ResourceName = resourceName
                });
            }
        }
        return apiResources;
    }
    #region 私有方法

    /// <summary>
    /// 获取路由地址名称
    /// </summary>
    /// <param name="controllerName">控制器地址</param>
    /// <param name="template">路由名称</param>
    /// <returns></returns>
    private static string GetRouteName(string controllerName, string template)
    {
        if (!template.StartsWith("/"))
            template = "/" + template;//如果路由名称不是/开头则加上/防止控制器没写
        if (template.Contains("[controller]"))
        {
            controllerName = controllerName.Replace("Controller", "");//去掉Controller
            controllerName = StringUtils.FirstCharToLower(controllerName);//转首字母小写写
            template = template.Replace("[controller]", controllerName);//替换[controller]
        }

        return template;
    }

    /// <summary>
    /// 获取下级列表
    /// </summary>
    /// <param name="menuId"></param>
    /// <param name="IsContainSelef">是否包含自己</param>
    /// <returns></returns>
    private async Task<List<SysMenu>> GetChildList(long menuId,bool IsContainSelef = false)
    {
        var menuList = await GetMenuList();
        var list = new List<SysMenu>();
        list = UtilMethod.GetChild(menuList, list, new List<long> { menuId });
        if (IsContainSelef)
        {
            var self = menuList.FirstOrDefault(it => it.Id == menuId);
            list.Add(self);
        }
        return list;
    }
    /// <summary>
    /// 检查输入是否合法
    /// </summary>
    /// <param name="menu"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    private async Task CheckInput(SysMenu menu,string action)
    {       
        var menuList = await GetMenuList();
        //判断上级组织是否存在
        if (menu.ParentId>0 && !menuList.Any(o => o.Id == menu.ParentId))
        {
            throw Oops.Bah(MenuErrorCode.M004);
        }
        //判断同一层级中是否已有同名组织
        if (menuList.Any(o => o.ParentId == menu.ParentId && o.Name == menu.Name && o.Id != menu.Id))
            throw Oops.Bah(MenuErrorCode.M002);

        //修改时需要判断修改后的父级组织是否在自己的下级组织列表里
        if (action == CommonConst.ACTION_UPDATE)
        {
            if (menu.Id == menu.ParentId)
                throw Oops.Bah(MenuErrorCode.M001);
            if (!menuList.Any(it => it.Id == menu.Id))
            {
                throw Oops.Bah(MenuErrorCode.M009);
            }
            var result = new List<SysMenu>();
            var child = await GetChildList(menu.Id);
            if (child.Any(it => it.Id == menu.ParentId))
            {
                throw Oops.Bah(MenuErrorCode.M003);
            }
        }
    }

    /// <summary>
    /// 清除菜单信息缓存
    /// </summary>
    private void RefreshCache()
    {
        _redisCacheManager.Remove(CacheConst.SysMenu);
    }
    /// <summary>
    /// 清除用户数据范围缓存
    /// </summary>
    /// <param name="menuIds"></param>
    /// <returns></returns>
    private async Task RemoveDataScopeCache(List<long> menuIds)
    {
        await _eventPublisher.PublishAsync(EventSubscriberConst.RemoveDataScopeByMenuIds, menuIds);
    }
    #endregion
}